﻿#include "scatterplotwidget.h"
#include "qcustomplot.h"
#include  <qspinbox.h>
#include <algorithm>
#include <omp.h>

struct ColorKey {
    QColor color;

    ColorKey() {}
    ColorKey(const QColor& c) : color(c) {}

    bool operator<(const ColorKey& other) const {
        return color.red() < other.color.red() ||
            (color.red() == other.color.red() && color.green() < other.color.green()) ||
            (color.red() == other.color.red() && color.green() == other.color.green() && color.blue() < other.color.blue());
    }
};


QColor inline rainbowColor(double v, double vmin, double vmax) {
    if (v < vmin) v = vmin;
    if (v > vmax) v = vmax;

    double range = vmax - vmin;
    double normalizedValue = (v - vmin) / range;

    // 将normalizedValue分成四个阶段，分别对应蓝、绿、黄、红
    double blue, green, red;
    if (normalizedValue < 0.25) {
        // 蓝色阶段
        blue = 1.0;
        green = normalizedValue * 4; // 线性增加
        red = 0.0;
    }
    else if (normalizedValue < 0.5) {
        // 绿色阶段
        blue = 1.0 - (normalizedValue - 0.25) * 4; // 线性减少
        green = 1.0;
        red = 0.0;
    }
    else if (normalizedValue < 0.75) {
        // 黄色阶段
        blue = 0.0;
        green = 1.0;
        red = (normalizedValue - 0.5) * 4; // 线性增加
    }
    else {
        // 红色阶段
        blue = 0.0;
        green = 1.0 - (normalizedValue - 0.75) * 4; // 线性减少
        red = 1.0;
    }

    return QColor::fromRgbF(red, green, blue);
}


ScatterPlotWidget::ScatterPlotWidget(const QVector<QVector<double>>& data, const QStringList& mcolumnNames, QWidget* parent) : columnData(data),columnNames(mcolumnNames), QWidget(parent) {
    // Sample data
    setupUI();
    populateListWidgets();

    connect(plotButton, &QPushButton::clicked, this, &ScatterPlotWidget::plotScatter);
    connect(xListWidget, &QListWidget::currentRowChanged, this, &ScatterPlotWidget::plotScatter);
    connect(yListWidget, &QListWidget::currentRowChanged, this, &ScatterPlotWidget::plotScatter);
    connect(colorListWidget, &QListWidget::currentRowChanged, this, &ScatterPlotWidget::plotScatter);
    connect(cbdspbYMin->checkBox, &QCheckBox::stateChanged, this, &ScatterPlotWidget::plotScatter);
    connect(cbdspbYMax->checkBox, &QCheckBox::stateChanged, this, &ScatterPlotWidget::plotScatter);   
    connect(cbdspbYMin->doubleSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ScatterPlotWidget::yRangeChanged);
    connect(cbdspbYMax->doubleSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ScatterPlotWidget::yRangeChanged);
    connect(spbPointSize, QOverload<int>::of(&QSpinBox::valueChanged), this, &ScatterPlotWidget::plotScatter);
    connect(spbFontSize, QOverload<int>::of(&QSpinBox::valueChanged), this, &ScatterPlotWidget::plotScatter);
}





void ScatterPlotWidget::setupUI() {
    auto mainLayout = new QHBoxLayout();
    this->setLayout(mainLayout);

    // Create the list widgets
    auto listLayout = new QHBoxLayout();
    xListWidget = new QListWidget(this);
    yListWidget = new QListWidget(this);
    colorListWidget = new QListWidget(this);
    listLayout->addWidget(xListWidget);
    listLayout->addWidget(yListWidget);
    listLayout->addWidget(colorListWidget);

    auto leftLayout = new QVBoxLayout();
    leftLayout->addLayout(listLayout);
    mainLayout->addLayout(leftLayout);

    cbdspbYMin = new CheckBoxDoubleSpinBox("Y Min");
    cbdspbYMin->doubleSpinBox->setValue(0);
    cbdspbYMax = new CheckBoxDoubleSpinBox("Y Max");
    cbdspbYMax->doubleSpinBox->setValue(8000);
    spbPointSize = new QSpinBox(this);
    spbPointSize->setValue(5);
    spbPointSize->setMinimum(1);
    spbPointSize->setMaximum(20);
    spbPointSize->setSingleStep(1);
    spbFontSize = new QSpinBox(this);
    spbFontSize->setValue(16);
    spbFontSize->setMinimum(1);
    spbFontSize->setMaximum(100);
    spbFontSize->setSingleStep(1);

    auto drawControlLayout = new QVBoxLayout();
    drawControlLayout->addWidget(cbdspbYMin);
    drawControlLayout->addWidget(cbdspbYMax);
    drawControlLayout->addWidget(new QLabel("Point:"));
    drawControlLayout->addWidget(spbPointSize);
    drawControlLayout->addWidget(new QLabel("Font:"));
    drawControlLayout->addWidget(spbFontSize);
    
    plotButton = new QPushButton("Plot Scatter", this);
    drawControlLayout->addWidget(plotButton);
    auto spacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
    drawControlLayout->addItem(spacer);

    customPlot = new QCustomPlot(this);
    mainLayout->addWidget(customPlot);
    mainLayout->addLayout(drawControlLayout);
    
    mainLayout->setStretch(0, 4);
    mainLayout->setStretch(1, 12);
    mainLayout->setStretch(2, 1);
    
}

void ScatterPlotWidget::populateListWidgets() {
    for (const QString& name : columnNames) {
        xListWidget->addItem(name);
        yListWidget->addItem(name);
        colorListWidget->addItem(name);
    }
    
}


void ScatterPlotWidget::plotScatter() {
    customPlot->setOpenGl(true);  // 启用 OpenGL 加速

    int xIndex = xListWidget->currentRow();
    int yIndex = yListWidget->currentRow();
    int colorIndex = colorListWidget->currentRow();

    if (xIndex < 0 || yIndex < 0 || colorIndex < 0) {
        //QMessageBox::warning(this, "Selection Error", "Please select data from all three lists.");
        return;
    }

    QVector<double> xData = columnData[xIndex];
    QVector<double> yData = columnData[yIndex];
    QVector<double> colorData = columnData[colorIndex];
    QVector<QColor> pointColors(colorData.size());
    double minValue = *std::min_element(colorData.begin(), colorData.end());
    double maxValue = *std::max_element(colorData.begin(), colorData.end());
    for (int i = 0; i < colorData.size(); ++i) {
        pointColors[i] = rainbowColor(colorData[i], minValue, maxValue);
    }


    customPlot->clearPlottables();
    customPlot->clearItems();

    // Create a QCPGraph object
    QCPGraph* graph = customPlot->addGraph();
    graph->setAdaptiveSampling(true);
    //set line style
    graph->setLineStyle(QCPGraph::lsNone);

    double yMin = *std::min_element(yData.begin(), yData.end());
    double yMax = *std::max_element(yData.begin(), yData.end());

    if (cbdspbYMin->checkBox->isChecked()) {
        yMin = cbdspbYMin->doubleSpinBox->value();
    }
    if (cbdspbYMax->checkBox->isChecked()) {
        yMax = cbdspbYMax->doubleSpinBox->value();
    }
    //set range
    customPlot->xAxis->setRange(*std::min_element(xData.begin(), xData.end()), *std::max_element(xData.begin(), xData.end()));
    customPlot->yAxis->setRange(yMin, yMax);

    // Set the scatter style
    int pointSize = spbPointSize->value();
    int pointCount = xData.size();

    // 分组颜色相同的点到一起
    QMap<ColorKey, QVector<QPair<double, double>>> colorGroups;
    for (int i = 0; i < pointCount; ++i)
    {
        colorGroups[ColorKey(pointColors[i])].append(qMakePair(xData[i], yData[i]));
    }

    for (auto it = colorGroups.begin(); it != colorGroups.end(); ++it)
    {
        QColor color = it.key().color;
        QVector<QPair<double, double>> points = it.value();
        QVector<double> xPoints, yPoints;

        for (const auto& point : points)
        {
            xPoints.append(point.first);
            yPoints.append(point.second);
        }

        QCPGraph* graph = customPlot->addGraph();
        graph->setData(xPoints, yPoints);
        graph->setPen(Qt::NoPen);  // 不绘制线条，只绘制点
        graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, color, Qt::NoPen, pointSize));
    }


    //set xaix label
    customPlot->xAxis->setLabel(columnNames[xIndex]);
    customPlot->yAxis->setLabel(columnNames[yIndex]);

    //set font size
    int fontSize = spbFontSize->value();
    customPlot->xAxis->setTickLabelFont(QFont("Arial", fontSize));
    customPlot->yAxis->setTickLabelFont(QFont("Arial", fontSize));
    customPlot->xAxis->setLabelFont(QFont("Arial", fontSize));
    customPlot->yAxis->setLabelFont(QFont("Arial", fontSize));

    customPlot->setInteractions(QCP::Interaction::iRangeDrag | QCP::Interaction::iRangeZoom);
    // Rescale the axes to fit the data
    //customPlot->rescaleAxes();

    // Replot the chart
    customPlot->replot();
}


void ScatterPlotWidget::yRangeChanged(double value) {
    plotScatter();
}